#ifndef B2D_IRESOURCEMANAGER_H
#define B2D_IRESOURCEMANAGER_H

#include "ResourceFactory.h"
#include "IUncopyable.h"

NS_B2D_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Global resource manager contains references to resource factories.
/// 		Handlers must be registered for all resources types that are to be managed by it.
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API IResourceManager
	: public IUncopyable
{
	B2D_INTERFACE(IResourceManager)

public:
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Registers a resource factory.
	///
	/// \param	factory	The factory to register.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void registerFactory(IResourceFactory& factory) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Unregisters the factory for given resource type.
	///
	/// \param	resourceType	The type of resource.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void unregisterFactory(const ObjectType& resourceType) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Unregisters the factory for given type of resource.
	///
	/// \tparam	T	Type of resource.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	void unregisterFactory(void)
	{
		static_assert(std::is_base_of<IResource, T>::value, "T must be a type of resource.");
		unregisterFactory(T::ResourceType);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets a resource.
	///
	/// \param	type	The type of resource.
	/// \param	name	The name of the resource.
	///
	/// \return	Null if it wasn't found, the resource otherwise.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual ResourcePtr getResource(const ObjectType& type, const StringId& name) const = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets a resource.
	///
	/// \tparam	typename T	Type of the resource.
	/// \param	name	The name  of the resource.
	///
	/// \return	Null if it wasn't found, the resource otherwise.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	std::shared_ptr<T> getResource(const StringId& name) const
	{
		static_assert(std::is_base_of<IResource, T>::value, "T must be a type of resource.");
		ResourcePtr r = getResource(T::ResourceType, name);
		return std::static_pointer_cast<T>(r);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets a resource. If it doesn't exist it will try to load it using the name as path.
	///
	/// \param	type	The type of resource.
	/// \param	name	The name/path of the resource.
	///
	/// \return	The resource if loaded successfully.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual ResourcePtr getOrLoadResource(const ObjectType& type, const StringId& name) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets a resource. If it doesn't exist it will try to load it using the name as path.
	///
	/// \tparam	typename T	Type of the resource.
	/// \param	name	The name/path of the resource.
	///
	/// \return	The resource if loaded successfully.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	std::shared_ptr<T> getOrLoadResource(const StringId& name)
	{
		static_assert(std::is_base_of<IResource, T>::value, "T must be a type of resource.");
		ResourcePtr r = getResource(T::ResourceType, name);
		return std::static_pointer_cast<T>(r);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Creates an empty resource with the possibility if adding it to the cache. If not
	/// 		adding to the cache then the name is optional.
	///
	/// \param	type	  	The type of resource.
	/// \param	name	  	The name of the resource.
	/// \param	addToCache	(optional) Flag to determine if it should be cached.
	///
	/// \return	The new empty resource.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual ResourcePtr createEmptyResource(const ObjectType& type, const StringId& name, bool addToCache = true) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Creates an empty resource with the possibility if adding it to the cache. If not
	/// 		adding to the cache then the name is optional.
	///
	/// \tparam	typename T	Type of resource.
	/// \param	name	  	The name of the resource.
	/// \param	addToCache	(optional) Flag to determine if it should be cached.
	///
	/// \return	The new empty resource.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	std::shared_ptr<T> createEmptyResource(const StringId& name, bool addToCache = true)
	{
		static_assert(std::is_base_of<IResource, T>::value, "T must be a type of resource.");
		ResourcePtr r = createEmptyResource(T::ResourceType, name, addToCache);
		return std::static_pointer_cast<T>(r);
	}


	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Adds an existing resource to the cache.
	///
	/// \param	res	The resource.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void addExistingResource(const ResourcePtr& res) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Removes a resource from the cache.
	///
	/// \param	type	The resource type.
	/// \param	name	The resource name.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void removeResource(const ObjectType& type, const StringId& name) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Removes a resource from the cache.
	///
	/// \tparam	typename T	The resource type.
	/// \param	name	The resource name.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	void removeResource(const StringId& name)
	{
		static_assert(std::is_base_of<IResource, T>::value, "T must be a type of resource.");
		removeResource(T::ResourceType, name);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Removes all resources from the cache.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void removeAll(void) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Removes all the resources of the given type from the cache.
	///
	/// \param	t	The type of resource.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void removeAllOfType(const ObjectType& t) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Removes the unreferenced resources from the cache (and therefore deletes them).
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void removeUnreferenced(void) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Removes the unreferenced resources of the given type from the cache (and therefore
	/// 		deletes them).
	///
	/// \param	t	The type of resource.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void removeUnreferencedOfType(const ObjectType& t) = 0;
};


NS_B2D_END

#endif